#include <ctype.h>
#include <kernel.h>
#include <stdlib.h>
#include <kbd.h>
#include <vga.h>
#include <hardware.h>
#include <timer.h>
#include <string.h>

dev_inst devhead;

handle_inst handlehead;

extern unsigned int volatile systick;

void hardware_init()
{
    init_mem();

    init_int();

    timer_init();

    NODE_INIT(&devhead);
    NODE_INIT(&handlehead);

    vga_text_init();

    init_kbd();
}

dev_inst * hw_dev_add(dev_inst * dev)
{
    dev_inst * ptr;

    if (dev == NULL)
        return NULL;

    for (ptr = NODE_NEXT(&devhead); ptr != NULL; ptr = NODE_NEXT(ptr))
        {
        if (strcmp(ptr->devname, dev->devname) == 0)
            {
            return NULL;
            }
        }

    LOCKRESET(dev);

    return NODE_ADDTAIL(dev, &devhead);
}

int gethandle(dev_inst * dev, int lock)
{
    unsigned int hash;
    handle_inst * ptr, * nh;
    int retry;

    hash = (unsigned int)dev;
    hash = hash << 4;
    hash = hash | (lock & 0xF);
    hash = hash ^ (lock & 0xFFFFFFF0);

    retry = 1;

    while(retry)
        {
        for (ptr = NODE_NEXT(&handlehead); ptr != NULL; ptr = NODE_NEXT(ptr))
            {
            if (ptr->handle == hash)
                break;
            }
        if (ptr != NULL)
            hash += 1 + systick;
        else
            retry = 0;
        }

    nh = malloc(sizeof(handle_inst));

    if (nh == NULL)
        return 0;

    nh->dev = dev;
    nh->handle = (int)hash;

    NODE_ADDNEXT(nh, &handlehead);

    return (int)hash;
}

handle_inst * findhandle(int handle)
{
    handle_inst * ptr;

    for (ptr = NODE_NEXT(&handlehead); ptr != NULL; ptr = NODE_NEXT(ptr))
        {
        if (ptr->handle == handle)
            break;
        }

    return ptr;
}

int open(char * devname, int mode, int flag)
{
    dev_inst * ptr;
    int ret;

    if (devname == NULL)
        return 0;

    for (ptr = NODE_NEXT(&devhead); ptr != NULL; ptr = NODE_NEXT(ptr))
        {
        if (strcmp(ptr->devname, devname) == 0)
            {
            break;
            }
        }

    if (ptr == NULL)
        return 0;

    LOCK(ptr);

    if (ptr->open == NULL)
        return 0;

    ret = gethandle(ptr, GETLOCK(ptr));

    ret = (int)(ptr->open)((void *)ret, mode, flag);

    return ret;
}

int close(int handle)
{
    dev_inst * ptr;
    handle_inst * hd;
    int ret;

    hd = findhandle(handle);

    if (hd->dev == NULL || hd->handle != handle)
        return ERROR;

    if (ptr == NULL)
        return ERROR;

    if (ptr->close == NULL)
        return ERROR;

    ret = (int)(ptr->close)((void *)handle);

    UNLOCK(ptr);

    return ret;
}

ssize_t read(int handle, char * bfr, size_t n)
{
    dev_inst * ptr;
    handle_inst * hd;
    ssize_t ret;

    hd = findhandle(handle);

    if (hd->dev == NULL || hd->handle != handle)
        return ERROR;

    if (ptr == NULL)
        return ERROR;

    if (ptr->read == NULL)
        return ERROR;

    ret = (int)(ptr->read)((void *)handle, bfr, n);

    return ret;
}

ssize_t write(int handle, char * bfr, size_t n)
{
    dev_inst * ptr;
    handle_inst * hd;
    ssize_t ret;

    hd = findhandle(handle);

    if (hd->dev == NULL || hd->handle != handle)
        return ERROR;

    if (ptr == NULL)
        return ERROR;

    if (ptr->write == NULL)
        return ERROR;

    ret = (int)(ptr->write)((void *)handle, bfr, n);

    return ret;
}

int ioctl(int handle, int mode, void * arg)
{
    dev_inst * ptr;
    handle_inst * hd;
    ssize_t ret;

    hd = findhandle(handle);

    if (hd->dev == NULL || hd->handle != handle)
        return ERROR;

    if (ptr == NULL)
        return ERROR;

    if (ptr->ioctl == NULL)
        return ERROR;

    ret = (int)(ptr->ioctl)((void *)handle, mode, arg);

    return ret;
}

